package tc.de.krawallmann;

import java.beans.ExceptionListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import tc.de.krawallmann.persistence.XMLPersistenceException;

/**
 * The Class Bean provides helper methods for classes implementing the Bean pattern.
 * @author KrawallMann
 * @version 1.0
 */
public class Bean {
	
	/** The PropertyChangeSupport. */
	protected transient final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
	
	/** The VetoableChangeSupport. */
	protected transient final VetoableChangeSupport vcs = new VetoableChangeSupport(this);

	/**
	 * Adds the PropertyChangeListener.
	 * 
	 * @param listener
	 *            the PropertyChangeListener to be added
	 */
	public void addPropertyChangeListener(final PropertyChangeListener listener) {
		pcs.addPropertyChangeListener(listener);
	}

	/**
	 * Adds the PropertyChangeListener.
	 * 
	 * @param property
	 *            the property to listen to
	 * @param listener
	 *            the PropertyChangeListener to be added
	 */
	public void addPropertyChangeListener(final String property, final PropertyChangeListener listener) {
		pcs.addPropertyChangeListener(property, listener);
	}

	/**
	 * Adds the VetoableChangeListener.
	 * 
	 * @param property
	 *            the property to listen to
	 * @param listener
	 *            the VetoableChangeListener to be added
	 */
	public void addVetoableChangeListener(final String property,
			final VetoableChangeListener listener) {
		vcs.addVetoableChangeListener(property, listener);
	}

	/**
	 * Adds the VetoableChangeListener.
	 * 
	 * @param listener
	 *            the VetoableChangeListener to be added
	 */
	public void addVetoableChangeListener(final VetoableChangeListener listener) {
		vcs.addVetoableChangeListener(listener);
	}

	/**
	 * Deserializes a Bean.
	 * 
	 * @param file
	 *            the file to be read from
	 * @return the bean which was read from the given file
	 * @throws XMLPersistenceException
	 *             if there was trouble reading the Bean (file might be corrupted!)
	 * @throws FileNotFoundException
	 *             if the file was not found
	 */
	public static Bean deserialize(final String file) throws XMLPersistenceException,
			FileNotFoundException {
		if (file == null) {
			throw new IllegalArgumentException("Bean#deserialize(String): Null parameter: file");
		}
		final InputStream inStream = new FileInputStream(file);
		final XMLDecoder decoder = new XMLDecoder(inStream);
		Object readObject;
		decoder.setExceptionListener(new ExceptionListener() {
			@Override
			public void exceptionThrown(final Exception exception) {
				// Empty!
			}
		});
		readObject = decoder.readObject();
		decoder.close();
		if (readObject == null) {
			throw new XMLPersistenceException("The object read from " + file + " is null!");
		}
		if (readObject instanceof Bean) {
			return (Bean) readObject;
		}
		throw new XMLPersistenceException("The object read from " + file
				+ " has the invalid class " + readObject.getClass() + "!");
	}

	/**
	 * Fire an indexed property change.
	 * 
	 * @param property
	 *            the property
	 * @param index
	 *            the index
	 * @param oldValue
	 *            the old value
	 * @param newValue
	 *            the new value
	 */
	public void fireIndexedPropertyChange(final String property, final int index, final Object oldValue,
			final Object newValue) {
		pcs.fireIndexedPropertyChange(property, index, oldValue, newValue);
	}

	/**
	 * Fire a property change.
	 * 
	 * @param property
	 *            the property
	 * @param oldValue
	 *            the old value
	 * @param newValue
	 *            the new value
	 */
	public void firePropertyChange(final String property, final Object oldValue, final Object newValue) {
		pcs.firePropertyChange(property, oldValue, newValue);
	}

	/**
	 * Fire a vetoable change.
	 * 
	 * @param property
	 *            the property
	 * @param oldValue
	 *            the old value
	 * @param newValue
	 *            the new value
	 * @throws PropertyVetoException
	 *            if a VetoableChangeListener vetoes
	 */
	public void fireVetoableChange(final String property, final Object oldValue,
			final Object newValue) throws PropertyVetoException {
		vcs.fireVetoableChange(property, oldValue, newValue);
	}

	/**
	 * Gets the PropertyChangeListener.
	 * 
	 * @return PropertyChangeListeners
	 */
	public PropertyChangeListener[] getPropertyChangeListeners() {
		return pcs.getPropertyChangeListeners();
	}

	/**
	 * Gets the PropertyChangeListener that listen to the given property.
	 * 
	 * @param property
	 *            the property
	 * @return the PropertyChangeListeners
	 */
	public PropertyChangeListener[] getPropertyChangeListeners(final String property) {
		return pcs.getPropertyChangeListeners(property);
	}

	/**
	 * Gets the VetoableChangeListener.
	 * 
	 * @return the VetoableChangeListeners
	 */
	public VetoableChangeListener[] getVetoableChangeListeners() {
		return vcs.getVetoableChangeListeners();
	}

	/**
	 * Gets the VetoableChangeListener that listen to the given property.
	 * 
	 * @param property
	 *            the property
	 * @return the VetoableChangeListeners
	 */
	public VetoableChangeListener[] getVetoableChangeListeners(final String property) {
		return vcs.getVetoableChangeListeners(property);
	}

	/**
	 * Checks for PropertyChangeListeners that listen to the given property.
	 * 
	 * @param property
	 *            the property
	 * @return true, if successful
	 */
	public boolean hasPropertyChangeListeners(final String property) {
		return pcs.hasListeners(property);
	}

	/**
	 * Checks for VetoableChangeListeners that listen to the given property.
	 * 
	 * @param property
	 *            the property
	 * @return true, if successful
	 */
	public boolean hasVetoableChangeListeners(final String property) {
		return vcs.hasListeners(property);
	}

	/**
	 * Removes the PropertyChangeListener.
	 * 
	 * @param listener
	 *            the PropertyChangeListener to be removed
	 */
	public void removePropertyChangeListener(final PropertyChangeListener listener) {
		pcs.removePropertyChangeListener(listener);
	}

	/**
	 * Removes the PropertyChangeListener from the event receivers list for the given property.
	 * 
	 * @param property
	 *            the property
	 * @param listener
	 *             the PropertyChangeListener to be removed
	 */
	public void removePropertyChangeListener(final String property, final PropertyChangeListener listener) {
		pcs.removePropertyChangeListener(property, listener);
	}

	/**
	 * Removes the VetoableChangeListener from the event receivers list for the given property.
	 * 
	 * @param property
	 *            the property
	 * @param listener
	 *             the VetoableChangeListener to be removed
	 */
	public void removeVetoableChangeListener(final String propertyName,
			final VetoableChangeListener listener) {
		vcs.removeVetoableChangeListener(propertyName, listener);
	}

	/**
	 * Removes the VetoableChangeListener.
	 * 
	 * @param listener
	 *            the VetoableChangeListener to be removed
	 */
	public void removeVetoableChangeListener(final VetoableChangeListener listener) {
		vcs.removeVetoableChangeListener(listener);
	}

	/**
	 * Serializes this Bean to the given file.
	 * 
	 * @param toSave
	 *            the bean to be saved
	 * @param file
	 *            the file in which the Bean should be saved
	 * @throws XMLPersistenceException
	 *             if there was trouble storing this Bean
	 * @throws FileNotFoundException
	 *             if the file does not exist
	 */
	public static void serialize(final Bean toSave, final String file) throws XMLPersistenceException,
			FileNotFoundException {
		if (file == null) {
			throw new IllegalArgumentException("Bean#serialize(Bean,String): Null parameter: file");
		}
		if (toSave == null) {
			throw new IllegalArgumentException(
					"Bean#serialize(Bean,String): Null parameter: toSave");
		}
		final OutputStream outStream = new FileOutputStream(file);
		final XMLEncoder encoder = new XMLEncoder(outStream);
		encoder.writeObject(toSave);
		encoder.close();
	}

}
